/**
 * @class UI-基础件-设置栏
 * @author DuskyHuang 圣子
 * @description 包含显示设置、时间控制、语言设置等UI操控，是最主要的头部栏
*/

import { _decorator, Node, Layout, Toggle, Sprite, tween, Vec3, UITransform, Button, Tween, profiler, EventTouch, ToggleContainer, gfx } from 'cc';
import { core, Mediator, Proxy, TweenSwitcher } from 'cfw';
import { Res, StarLevel, StarNameType, Timer } from 'const';
import { Task, TaskNames, UI } from 'notifications';
import { gizmos, constStars, timer, ui } from 'proxies';
import { CultureQuad } from '../../stellar/culture-quad';
const { ccclass, property } = _decorator;

@ccclass('cocosmos.setting-menu')
export class SettingMenu extends Mediator {

    @property(Node) public settingTgl: Node = null;
    @property(Node) public settingList: Node = null;
    @property(Node) public subList: Node = null;
    @property(TweenSwitcher) public topLine: TweenSwitcher = null;

    protected onLoad(): void {
        this._init();
        this._listen();
    }

    protected toggleLanguage(lang: Toggle): void {
        this.notify(UI.LANGUAGE, lang.isChecked ? 'en' : 'cn');
    }

    protected toggleFullscreen(fs: Toggle): void {
        const fullscreen = fs.isChecked;
        fs.getComponent(Sprite).enabled = !fullscreen;
        this.notify(UI.FULL_SCREEN, fullscreen);
        core.root.fullScreen = fullscreen;
    }

    protected start(): void {
        this.settingList.active = false;
        this.settingList.getComponent(Layout).enabled = false;
        this._initToggleEventAndStates();
        this._initTimerEventAndStates();
        this._initLabelsEventAndStates();
        this._setToggleDefault(this.settingList.getChildByName('语言').getComponent(Toggle), ui.language === 'en');
    }

    private _init(): void {
        this._twn_list = this.settingList.getComponentsInChildren(TweenSwitcher);
        this._btn_list = this.settingList.getComponentsInChildren(Button);
        this._twn_list.forEach((twn, i) => {
            twn.showDistance = twn.hideDistance = i * DIST_SLIDE;
            twn.showDuration = twn.hideDuration = DURATION;
        });
        this._twn_tgl = this.settingTgl.getComponent(TweenSwitcher);
        this.scheduleOnce(() => this.settingList.getComponent(UITransform)
            .convertToNodeSpaceAR(this.settingTgl.worldPosition, subPos));
    }

    private _listen(): void {
        this.settingList.getComponentsInChildren(Toggle).forEach(tgl =>
            tgl.node.on(Toggle.EventType.TOGGLE, this.toggleSetting, this));
    }

    protected toggleList(tgl: Toggle): void {
        tgl.getComponent(Sprite).enabled = !tgl.isChecked;
        this.unscheduleAllCallbacks();
        if (tgl.isChecked) {
            this.settingList.active = true;
            this._shows(this._twn_list, TweenSwitcher.EffectType.FADE_SLIDE);
        } else {
            this.scheduleOnce(() => this.settingList.active = false, DURATION);
            this._hides(this._twn_list, TweenSwitcher.EffectType.FADE_SLIDE);
        }
    }

    private _checkCultTexTask(name: string): void {
        if (name === '星际' && !CultureQuad.LoadTexTaskLaunched) {
            CultureQuad.LoadTexTaskLaunched = true;
            this.notify(Task.Load.DYNAMIC_TEX, TaskNames.TEX_CONST_NORTH);
            this.notify(Task.Load.DYNAMIC_TEX, TaskNames.TEX_CONST_SOUTH);
        }
    }

    protected toggleSetting(tgl: Toggle): void {
        tgl.getComponent(Sprite).enabled = !tgl.isChecked;
        const list = this.subList.children.find(list => list.name === tgl.node.name);
        if (list) {
            const otherTwns = this._twn_list.copy().remove(tgl.getComponent(TweenSwitcher));
            const otherBtns = this._btn_list.copy().remove(tgl.getComponent(Button));
            if (tgl.isChecked) {
                this.subList.children.forEach(child => child.active = false);
                this._twn_tgl.hide();
                this._hides(otherTwns, TweenSwitcher.EffectType.FADE);
                this._interactable(otherBtns, false);
                this._slide(tgl, true);
                list.getComponent(TweenSwitcher).show(true);
                this._checkCultTexTask(tgl.node.name);
            } else {
                this._twn_tgl.show();
                this._interactable(otherBtns, true);
                this._shows(otherTwns, TweenSwitcher.EffectType.FADE);
                this._slide(tgl, false);
                list.getComponent(TweenSwitcher).hide(true);
            }
        }
    }

    protected toggleStarNameType(tgl: Toggle): void {
        tgl.getComponent(Sprite).enabled = !tgl.isChecked;
        const type = +tgl.node.name as StarNameType;
        const ntf = UI.Labels.STAR_NAME_TYPE;
        if (tgl.isChecked) {
            console.log('=>', ntf, type);
            this.notify(ntf, type);
        }
    }

    protected toggleStarNameLevel(tgl: Toggle): void {
        tgl.getComponent(Sprite).enabled = !tgl.isChecked;
        const level = +tgl.node.name as StarLevel;
        const ntf = UI.Labels.STAR_NAME_LEVEL;
        if (tgl.isChecked) {
            console.log('=>', ntf, level);
            this.notify(ntf, level);
        } else {
            const any = tgl.node.parent.getComponent(ToggleContainer).anyTogglesChecked();
            if (!any) {
                console.log('=>', ntf, StarLevel.NONE);
                this.notify(ntf, StarLevel.NONE);
            }
        }
    }

    protected openLensPanel(): void {
        this.notify(UI.Panels.OPEN, Res.Prefabs.LENS_PANEL);
    }

    protected onTimer(tgl: Toggle): void {
        tgl.getComponent(Sprite).enabled = !tgl.isChecked;
        const idx = tgl.node.name;
        timer.accelerate(+idx as Timer);
    }

    private _interactable(btns: Button[], interactable: boolean): void {
        btns.forEach(btn => btn.interactable = interactable);
    }

    private _slide(sub: Toggle, left: boolean): void {
        Tween.stopAllByTarget(sub.node);
        if (left) {
            lastPos.x = sub.node.position.x;
            tween(sub.node).to(DURATION, { position: subPos }, { easing: 'sineOut' }).start();
        } else {
            tween(sub.node).to(DURATION, { position: lastPos }, { easing: 'sineOut' }).start();
        }
    }

    private _shows(tws: TweenSwitcher[], type?: Values<typeof TweenSwitcher.EffectType>, untouchable?: boolean): void {
        if (!isNullish(type)) tws.forEach(twn => twn.showType = type);
        if (untouchable) tws.forEach(twn => {
            const btn = twn.getComponent(Button);
            if (btn) btn.interactable = true;
        });
        tws.forEach(twn => twn.show());
    }

    private _hides(tws: TweenSwitcher[], type?: Values<typeof TweenSwitcher.EffectType>, touchable?: boolean): void {
        if (!isNullish(type)) tws.forEach(twn => twn.hideType = type);
        tws.forEach(twn => twn.hide());
    }

    protected toggleMusic(tgl: Toggle): void {
        const mute = !tgl.isChecked;
        tgl.getComponent(Sprite).enabled = mute;
        core.audio.mute = mute;
    }

    protected toggleStats(tgl: Toggle): void {
        const hide = !tgl.isChecked;
        tgl.getComponent(Sprite).enabled = hide;
        hide ? profiler.hideStats() : profiler.showStats();
    }

    protected clickGuide(): void {
        this.notify(UI.Panels.OPEN, Res.Prefabs.GUIDE_PANEL);
    }

    protected clickBorders(): void {
        this.notify(UI.Popups.TIP_LINE, 'todos@dev');
    }

    protected clickSync(): void {
        this.notify(UI.Popups.TIP_LINE, 'todos@dev');
    }

    protected clickStory(): void {
        this.notify(UI.Popups.TIP_LINE, 'todos@stories');
    }

    protected toggleInSubList(tgl: Toggle): void {
        tgl.getComponent(Sprite).enabled = !tgl.isChecked;
        const arg: boolean = tgl.isChecked;
        const { name, parent: { name: about } } = tgl.node;
        if (name === '天球') this._relate(tgl, '时角', false);
        if (name === '时角') this._relate(tgl, '天球', false);
        const ntf = `UI:${about}.${name}`;
        console.log('=>', ntf, arg);
        this.notify(ntf, arg);
    }

    protected clickStars(event: EventTouch): void {
        const toggle = (event.target as Node).getComponent(Toggle);
        // notice: clickEvent before toggleEvent, so toggle is unchecked now!
        const toCheck = !toggle.isChecked;
        if (toCheck && constStars.showConstLines) this._relate(toggle, '连线', true);
        else if (!toCheck && constStars.showConstLines) this._relate(toggle, '连线', false);
    }

    protected clickLines(event: EventTouch): void {
        const toggle = (event.target as Node).getComponent(Toggle);
        // notice: clickEvent before toggleEvent, so toggle is unchecked now!
        const toCheck = !toggle.isChecked;
        if (toCheck && !constStars.showStars) {
            this.notify(UI.Stellar.STARS, true);
            this._relate(toggle, '恒星', true);
        }
    }

    private _initLabelsEventAndStates(): void {
        const labels = this.subList.getChildByName('名称');
        labels.children.forEach(child => {
            const tgl = child.getComponent(Toggle);
            if (tgl) this._setToggleDefault(tgl, ui.getStateByName(tgl.node.name), true);
        });
        const types = labels.getChildByName('命名');
        types.getComponentsInChildren(Toggle).forEach(tgl => {
            tgl.node.on(Toggle.EventType.TOGGLE, this.toggleStarNameType, this);
            this._setToggleDefault(tgl, (+tgl.node.name) == ui.starNameType);
        });
        types.getComponent(ToggleContainer).allowSwitchOff = false;
        labels.getChildByName('星等').getComponentsInChildren(Toggle).forEach(tgl => {
            tgl.node.on(Toggle.EventType.TOGGLE, this.toggleStarNameLevel, this);
            this._setToggleDefault(tgl, (+tgl.node.name) == ui.starNameLevel);
        });
    }

    private _initTimerEventAndStates(): void {
        const times = this.subList.getChildByName('时间');
        times.getComponentsInChildren(Toggle).forEach(toggle => {
            const name = +toggle.node.name as Timer;
            this._setToggleDefault(toggle.getComponent(Toggle), timer.timer === name);
            toggle.node.on(Toggle.EventType.TOGGLE, this.onTimer, this);
        });
        times.getComponent(ToggleContainer).allowSwitchOff = false;
    }

    private _initToggleEventAndStates(): void {
        this._initToggleEventsAndState('标尺', gizmos);
        this._initToggleEventsAndState('坐标', gizmos);
        this._initToggleEventsAndState('星际', constStars);
    }

    private _initToggleEventsAndState(sub: string, proxy: { getStateByName: Function } & Proxy): void {
        this.subList.getChildByName(sub).getComponentsInChildren(Toggle).forEach(toggle => {
            this._setToggleDefault(toggle, proxy.getStateByName(toggle.node.name), true)
        });
    }

    private _relate(checker: Toggle, to: string, checked: boolean): void {
        const toggle = checker.node.parent?.getChildByName(to)?.getComponent(Toggle);
        if (toggle) {
            toggle.setIsCheckedWithoutNotify(checked);
            toggle.getComponent(Sprite).enabled = !checked;
        }
    }

    private _setToggleDefault(tgl: Toggle, checked: boolean, handle: boolean = false): void {
        tgl.setIsCheckedWithoutNotify(checked);
        tgl.getComponent(Sprite).enabled = !checked;
        handle && tgl.node.on(Toggle.EventType.TOGGLE, this.toggleInSubList, this);
    }

    private _twn_list: TweenSwitcher[];
    private _btn_list: Button[];
    private _twn_tgl: TweenSwitcher;;
}

const DIST_SLIDE: num.px = 100;
const DURATION: num.px = 0.3;
const subPos = new Vec3;
const lastPos = new Vec3;